package org.xtext.runtime.term;

import java.util.Map;

import org.xtext.runtime.type.IType;
import org.xtext.runtime.type.ObjectType;
import org.xtext.runtime.type.Type;
import org.xtext.runtime.util.Bindings;
import org.xtext.runtime.util.ILogicVisitor;


public class Term<T> implements ITerm {
	private T value;
	private IType type;
	
	@SuppressWarnings("rawtypes")
	public static ITerm newInstance(Object value) {
		IType type = Type.getType(value.getClass());
		
		// If there is now predefined type, default to object type...
		if (type == null) {
			type = new ObjectType(value.getClass());
		}
		
		if (type.equals(Type.listType)) {
			java.util.List list = (java.util.List) value;
			ITerm[] terms = new ITerm[list.size()];
			for (int i=0; i<terms.length; i++) {
				terms[i] = Term.newInstance(list.get(i));
			}
			return new List(terms);
		}
		return Term.newInstance(type, value);
	}
	
	public static <T> Term<T> newInstance(IType type, T value) {
		if (type.typeCheck(value)) {
			return new Term<T>(type, value);
		}
		return null;
	}
	
	public Term() {
	}
	
	public Term(IType type, T value) {
		this.type = type;
		this.value = value;
	}
	
	public IType type() {
		return type;
	}
	
	public T value() {
		return value;
	}
	
	public String toString() {
		if (type.equals(Type.stringType)) {
			return "\"" + value + "\"";
		}
		return "" + value;
	}

	public boolean equals(Object object) {
		if (Term.class.isInstance(object)) {
			Term<?> term = (Term<?>) object;
			return type.equals(term.type) && value.equals(term.value);
		}
		return false;
	}

	/**
	 * Used for copying values - but here no copy is required because this
	 * type of term holds a constant value...
	 */
	@Override
	public Term<T> copy() {
		return this;
	}

	@Override
	public Term<T> copy(Map<String, Variable> vars) {
		return copy();
	}

	public boolean hasFreeVariables() {
		return false;
	}

	@Override
	public ITerm apply(Bindings bindings) {
		return this;
	}
	
	@Override
	public Object accept(ILogicVisitor visitor) {
		return visitor.visit(this);
	}
}
